package com.listeningframework.boot.codegen.generators.coders;

import com.listeningframework.boot.codegen.constants.Templates;
import com.listeningframework.boot.codegen.context.Context;
import com.listeningframework.boot.codegen.context.EntityContext;
import com.listeningframework.boot.codegen.context.ModuleContext;
import com.listeningframework.boot.codegen.context.ProjectContext;
import com.listeningframework.boot.codegen.exception.CodeGenException;
import com.listeningframework.boot.codegen.utils.CodeGenUtils;
import com.listeningframework.boot.codegen.utils.FreemarkerUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * Abstract Gnerators
 *
 * @author lixining
 * @version $Id: AbstractGenerator.java, v 0.1 2016年12月1日 下午7:17:57 lixining Exp $
 */
public abstract class AbstractCoder {
    /**
     * 包名
     */
    protected static final String CLASS_PACKAGE = "classPackage";
    /**
     * 模块功能描述
     */
    protected static final String MODEL_DESC = "modelDesc";
    /**
     * 模型名称前缀
     */
    protected static final String MODEL_PREFIX = "modelPrefix";
    /**
     * 模型后缀
     */
    protected static final String MODEL_SUFFIX = "modelSuffix";
    /**
     * 导入类库
     */
    protected static final String CLASS_IMPORTS = "classImports";
    /**
     * Metadata List
     */
    protected static final String CLASS_METADATA_LIST = "classMetaDataList";
    /**
     * 当前时间
     */
    protected static final String CURRENT_DATETIME = "currentDateTime";
    /**
     * Template Path
     */
    private static final String TEMPLATE = "/templates/";
    /**
     * Entity Context
     */
    protected EntityContext entityContext;

    public AbstractCoder(EntityContext entityContext) {
        this.entityContext = entityContext;
    }

    /**
     * Generate Template Params
     *
     * @return Map
     */
    protected abstract Map<String, Object> getParams(Templates template) throws CodeGenException;

    /**
     * Generate Content
     *
     * @return String
     */
    protected abstract String getContent(Templates template) throws CodeGenException;

    /**
     * 写源码文件到磁盘
     *
     * @throws CodeGenException 生成异常
     */
    public void generate() throws CodeGenException {
        try {
            if (!entityContext.isEnabled()) {
                return;
            }
            Templates template = getTemplateType();
            ModuleContext moduleContext = Context.getContext().getModuleContext();
            boolean isMapper = (this instanceof MapperCoder);
            // 2. 如果是MapperCoder 而且持久化技术不是Mybatis,直接返回无需生成
            if (isMapper && !moduleContext.isOrmMyBatis()) {
                return;
            }
            boolean override = entityContext.isOverride();// 是否覆盖
            String absPath = getFileSavePath(template);
            File destFile = new File(absPath);
            String content = getContent(template);
            System.err.println(content);
            if (!destFile.exists() || override) {
                FileUtils.write(destFile, content, Charset.forName("utf-8"));
            }
        } catch (Exception e) {
            throw new CodeGenException("write content fail", e);
        }
    }

    private Templates getTemplateType() {
        if (this instanceof EntityCoder) {
            return Templates.ENTITY;
        }
        return null;
    }

    /**
     * 渲染Freemarker模板
     *
     * @param template 模板
     * @return String
     * @throws CodeGenException 异常
     */
    protected String render(Templates template) throws CodeGenException {
        InputStream input = null;
        try {
            Map<String, Object> params = getParams(template);
            String ormName = StringUtils.lowerCase(Context.getContext().getModuleContext().getOrm());
            String templateContent = "";
            String defaultTemplateName = TEMPLATE + ormName + "_" + template.getTemplate();
            // 读取自定义模板
            ProjectContext projectContext = Context.getContext().getProjectContext();
            String customTemplateLocation = projectContext.getSrcLocation() + projectContext.getConfigLocation() + "/templates/";
            if (template.isController() && entityContext.isCustomControllerTemplate()) {
                String customTemplateName = entityContext.getCustomControllerTemplateName();
                templateContent = FileUtils.readFileToString(new File(customTemplateLocation + customTemplateName), "utf-8");
            } else if (template.isService() && entityContext.isCustomServiceTemplate()) {
                String customTemplateName = entityContext.getCustomServiceTemplateName();
                templateContent = FileUtils.readFileToString(new File(customTemplateLocation + customTemplateName), "utf-8");
            } else if (template.isDao() && entityContext.isCustomDaoTemplate()) {
                String customTemplateName = entityContext.getCustomDaoTemplateName();
                templateContent = FileUtils.readFileToString(new File(customTemplateLocation + customTemplateName), "utf-8");
            } else if (template.isEntity() && entityContext.isCustomEntityTemplate()) {
                String customTemplateName = entityContext.getCustomEntityTemplateName();
                templateContent = FileUtils.readFileToString(new File(customTemplateLocation + customTemplateName), "utf-8");
            } else if (template.isDomain() && entityContext.isCustomDomainTemplate()) {
                String customTemplateName = entityContext.getCustomDomainTemplateName();
                templateContent = FileUtils.readFileToString(new File(customTemplateLocation + customTemplateName), "utf-8");
            } else {
                input = AbstractCoder.class.getResourceAsStream(defaultTemplateName);
                templateContent = input == null ? "" : IOUtils.toString(input, "utf-8");
            }
            return FreemarkerUtils.getFreemarkerContent(templateContent, params);
        } catch (CodeGenException e) {
            throw e;
        } catch (Exception e) {
            throw new CodeGenException("模板渲染失败", e);
        } finally {
            IOUtils.closeQuietly(input);
        }
    }


    /**
     * 获取Common的模板参数
     *
     * @return Map
     */
    protected Map<String, Object> newHashMap(Templates template) {
        ProjectContext projectContext = Context.getContext().getProjectContext();
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put(MODEL_PREFIX, entityContext.getPrefix());
        paramMap.put(MODEL_SUFFIX, entityContext.getSuffix());
        paramMap.put(MODEL_DESC, entityContext.getDesc());
        paramMap.put(CURRENT_DATETIME, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));

        // 子包
        String subpackage = CodeGenUtils.getPadSubPackage(entityContext.getSubPackage());
        if (template.isController()) {
            paramMap.put(CLASS_PACKAGE, projectContext.getWebPackage() + subpackage);
        }
        if (template.isService()) {
            paramMap.put(CLASS_PACKAGE, projectContext.getServicePackage() + subpackage);
        }
        if (template.isDao()) {
            paramMap.put(CLASS_PACKAGE, projectContext.getDaoPackage() + subpackage);
        }
        if (template.isDomain()) {
            paramMap.put(CLASS_PACKAGE, projectContext.getDomainPackage() + subpackage);
        }
        if (template.isEntity()) {
            paramMap.put(CLASS_PACKAGE, projectContext.getEntityPackage() + subpackage);
        }
        return paramMap;
    }

    /**
     * Get Package Name
     *
     * @param type 类型
     * @return String
     */
    protected String getPackageJavaName(Templates type) {
        ProjectContext projectContext = Context.getContext().getProjectContext();
        String entityCodeName = entityContext.getCodeFileTypeName(type, false);
        if (type.isController()) {
            return projectContext.getWebPackage() + "." + entityCodeName;
        }
        if (type.isService()) {
            return projectContext.getServicePackage() + "." + entityCodeName;
        }
        if (type.isDao()) {
            return projectContext.getDaoPackage() + "." + entityCodeName;
        }
        if (type.isDomain()) {
            return projectContext.getDomainPackage() + "." + entityCodeName;
        }
        if (type.isEntity()) {
            return projectContext.getEntityPackage() + "." + entityCodeName;
        }
        return null;
    }

    /**
     * 获取文件保存路径
     *
     * @param type 文件类型
     * @return String
     */
    protected String getFileSavePath(Templates type) {
        ProjectContext projectContext = Context.getContext().getProjectContext();
        if (type.isMapper()) {
            return projectContext.getSrcResourcesPath() + projectContext.getEntityPackage() + type.getSuffix() + type.getFileSuffix();
        }
        String srcLocation = projectContext.getSrcJavaPath();
        String packgeName = getPackageJavaName(type);
        return srcLocation + CodeGenUtils.package2Path(packgeName) + type.getFileSuffix();
    }
}
